/*
 * File Name:fs_test.c
 * Version:v1.0.0
 * Change Logs:
 * Date             Author          main change
 * 2025-03-04       lanxin          the first version
 *
*/

/********************************************* READ ME BEGIN *************************************************************/

/* Brief:
 *
 * *1.对 module_fs 的测试程序。
 *
 */

/* Notice:
 *
 * *
 *
 */

/********************************************* READ ME END ***************************************************************/

/* source file */
#include "module_fs.h"
#include "stdio.h"

/* debug define */

/* private define */
#define RAM_DISK_SIZE_BYTE      4096*1024*2
#define TEST_FILE_LEN           (8192)

/* function declaration */

/* variable declaration */
#ifdef ENABLE_ROM_FS_API
static const struct romfs_dirent _romfs_root[] =
{
    {ROMFS_DIRENT_DIR, "flash", RT_NULL, 0},
    {ROMFS_DIRENT_DIR, "sdcard", RT_NULL, 0}
};

const struct romfs_dirent romfs_root =
{
    ROMFS_DIRENT_DIR, "/", ( rt_uint8_t * ) _romfs_root, sizeof ( _romfs_root ) / sizeof ( _romfs_root[0] )
};
#endif

static uint8_t *p_buf = 0;

//do_mount rom /
//do_mount dev /sdcard sd
static int do_mount ( int argc, char **argv )
{
    if ( argc == 3 )
    {
#ifdef ENABLE_ROM_FS_API

        if ( rt_strcmp ( "rom", argv[1] ) == 0 )
        {
            fs_mount_at_rom ( argv[2], ( const void * ) &romfs_root );
            return 0;
        }

#endif

#ifdef ENABLE_FAL_FS_API

        if ( rt_strcmp ( "fal", argv[1] ) == 0 )
        {
            fs_mount_at_fal_partition ( "romfs", argv[2], "elm" );
            return 0;
        }

#endif

#ifdef ENABLE_RAM_FS_API

        if ( rt_strcmp ( "ram", argv[1] ) == 0 )
        {
            fs_mount_at_ram ( "ramdisk", argv[2], "elm", RAM_DISK_SIZE_BYTE );
            return 0;
        }

#endif
    }

    else if ( argc == 4 )
    {
        if ( rt_strcmp ( "dev", argv[1] ) == 0 )
        {
            fs_mount ( argv[3], argv[2], "elm" );
            return 0;
        }
    }

    return 0;
}

//do_unmount /sd
static int do_unmount ( int argc, char **argv )
{
    if ( argc != 2 )
        return -1;

    fs_unmount ( argv[1] );

    return 0;
}

//do_mkfs sd elm
static int do_mkfs ( int argc, char **argv )
{
    if ( argc != 3 )
        return -1;

    return fs_mkfs ( argv[1], argv[2] );
}

//do_delete_file /sd/test.txt
static int do_delete_file ( int argc, char **argv )
{
    if ( argc != 2 )
        return -1;

    fs_delete_file ( argv[1] );
    return -1;
}

//do_delete_dir /sd/test yes
static int do_delete_dir ( int argc, char **argv )
{
    if ( argc != 3 )
        return -1;

    fs_delete_dir_file ( argv[1], ( rt_strcmp ( argv[2], "yes" ) == 0 ) );
    return 0;
}

//do_create_dir dir /sd/data --> /sd/data
//do_create_dir path /sd/data/test.txt -->/sd/data
static int do_create_dir ( int argc, char **argv )
{
    if ( argc != 3 )
        return -1;

    if ( rt_strcmp ( argv[1], "dir" ) == 0 )
        fs_create_dir ( argv[2] );
    else if ( rt_strcmp ( argv[1], "path" ) == 0 )
        fs_create_dir_by_file_path ( argv[2] );

    return 0;
}

static int do_create_file ( int argc, char **argv )
{
    if ( argc != 2 )
        return -1;

    return fs_create_file ( argv[1] );
}

//do_write /sd/test.txt
static int do_write ( int argc, char **argv )
{
    if ( argc != 2 )
        return -1;

    if ( fs_get_file_access ( argv[1] ) )
        fs_delete_file ( argv[1] );

    fs_create_file ( argv[1] );
    uint8_t w_buf[] = {1, 2, 3, 4, 5, 6};
    uint8_t w_buf1[] = {9, 10, 11, 12, 13, 14};

    fs_write ( argv[1], 0, ( void * ) w_buf, 6 );
    fs_write ( argv[1], 10, ( void * ) w_buf1, 6 );

    uint8_t r_buf[20];
    uint8_t r_buf1[20];
    rt_memset ( r_buf, 0, 20 );
    rt_memset ( r_buf1, 0, 20 );

    fs_read ( argv[1], 0, r_buf, 6 );
    fs_read ( argv[1], 10, r_buf1, 6 );

    for ( uint8_t i = 0; i < 6; i++ )
    {
        rt_kprintf ( "%d%s", r_buf[i], i <= 5 ? " " : "\r\n" );
    }

    for ( uint8_t i = 0; i < 6; i++ )
    {
        rt_kprintf ( "%d%s", r_buf1[i], i <= 5 ? " " : "\r\n" );
    }

    return 0;
}

//do_write_with_create /sd/test.txt
static int do_write_with_create ( int argc, char **argv )
{
    if ( argc != 2 )
        return -1;

    if ( fs_get_file_access ( argv[1] ) )
        fs_delete_file ( argv[1] );

    char *w_buf = "123456";
    char *w_buf1 = "abcdef";
    fs_write_with_create ( argv[1], 0, ( void * ) w_buf, rt_strlen ( w_buf ) );
    fs_write_with_create ( argv[1], 10, ( void * ) w_buf1, rt_strlen ( w_buf1 ) );

    char buf1[20], buf2[20];

    fs_read ( argv[1], 0, buf1, 6 );
    fs_read ( argv[1], 10, buf2, 6 );
    rt_kprintf ( "buf1 %s buf2 %s\r\n", buf1, buf2 );

    return 0;
}

//do_write_append /sd/test.txt
static int do_write_append ( int argc, char **argv )
{
    if ( argc != 2 )
        return -1;

    if ( fs_get_file_access ( argv[1] ) == 0 )
        fs_create_file ( argv[1] );

    char *w_buf = "123456";
    fs_write_append ( argv[1], ( void * ) w_buf, rt_strlen ( w_buf ) );
    return 0;
}

//do_write_replace /sd/test.txt
static int do_write_replace ( int argc, char **argv )
{
    if ( argc != 2 )
        return -1;

    char *w_buf = "replace";
    fs_write_O_WRONLY_O_TRUNC ( argv[1], w_buf, rt_strlen ( w_buf ) );
    return 0;
}

//do_read /sd/test.txt
static int do_read ( int argc, char **argv )
{
    if ( argc != 2 )
        return -1;

    char *buf = 0;
    ssize_t r_size = fs_read_all ( argv[1], &buf );
    rt_kprintf ( "read size:%d val:%s\r\n", r_size, buf );

    if ( buf )
        rt_free ( buf );

    return 0;
}

//do_copy_file / /sd test.txt
static int do_copy_file ( int argc, char **argv )
{
    if ( argc != 4 )
        return -1;

    return fs_copy_file ( argv[1], argv[2], argv[3] );
}

//do_copy_dir_file / /sd/config
static int do_copy_dir_file ( int argc, char **argv )
{
    if ( argc != 3 )
        return -1;

    return fs_copy_dir_file ( argv[1], argv[2] );
}

static int do_get_state ( int argc, char **argv )
{
    if ( argc != 3 )
        return -1;

    if ( rt_strcmp ( argv[1], "access" ) == 0 )
    {
        int res = fs_get_file_access ( argv[2] );
        rt_kprintf ( "file %s is %s\r\n", argv[2], res ? "exist" : "not exist" );
        return 0;
    }
    else if ( rt_strcmp ( argv[1], "dir" ) == 0 )
    {
        int res = fs_get_dir_exist ( argv[2] );
        rt_kprintf ( "dir %s is %s\r\n", argv[2], res ? "exist" : "not exist" );
        return 0;
    }
    else if ( rt_strcmp ( argv[1], "size" ) == 0 )
    {
        ssize_t size = fs_get_file_size ( argv[2] );
        rt_kprintf ( "file %s size is %d\r\n", argv[2], size );
        return 0;
    }
    else if ( rt_strcmp ( argv[1], "capacity" ) == 0 )
    {
        int capacity = fs_get_capacity ( argv[2] );
        rt_kprintf ( "%s usage is %d\r\n", argv[2], capacity );
        return 0;
    }
    else if ( rt_strcmp ( argv[1], "dirfile" ) == 0 )
    {
        int res = fs_get_dir_file_exist ( argv[2] );
        rt_kprintf ( "dir %s %s\r\n", argv[2], res ? "have file" : "no file" );
        return 0;
    }

    return 0;
}

//do_get_file_cnt /sd
static int do_get_file_cnt ( int argc, char **argv )
{
    if ( argc != 2 )
        return -1;
    
    int cnt = fs_get_dir_file_num ( argv[1] );
    rt_kprintf ( "here is %d file under %s\r\n", cnt, argv[1] );
    return 0;
}

static void handle_event_cb ( int event, void *para )
{
    if ( E_FS_EVENT_READ_FAILED == event )
    {
        rt_kprintf ( "fs event:%d para:%s\r\n", event, ( char * ) para );
    }

    fs_clear_event_cb();
}

static int do_event_cb ( void )
{
    fs_set_event_cb ( handle_event_cb );

    return 0;
}

//一次性写入文件
static int do_file_test ( void )
{
    if ( p_buf )
        rt_free ( p_buf );

    p_buf = ( uint8_t * ) rt_calloc ( 1, TEST_FILE_LEN );

    if ( p_buf == 0 )
        return -1;

    for ( int i = 0; i < TEST_FILE_LEN; i++ )
    {
        p_buf[i] = ( i / 512 ) + '0';
    }

    int cnt = 10;
    char path[32];

    while ( cnt-- )
    {
        rt_memset ( path, 0, 32 );
        rt_sprintf ( path, "/sd/test_%d.bin", cnt );

        if ( fs_get_file_access ( path ) )
            fs_delete_file ( path );

        rt_tick_t t_b = rt_tick_get();
        fs_write_with_create ( path, 0, ( void * ) p_buf, TEST_FILE_LEN );
        rt_tick_t t_e = rt_tick_get();
        float MB = ( float ) TEST_FILE_LEN / 1024.0 / 1024.0 ;
        float t = ( float ) ( t_e - t_b ) / 1000.0;
        float speed = MB / t;
        printf ( "write %d KB take time:%d ms,speed %f MB/S\r\n", ( TEST_FILE_LEN / 1024 ), ( int ) ( t_e - t_b ), speed );
    }

    rt_free ( p_buf );
    p_buf = 0;

    return -1;
}

//追加写入
static int do_file_write_append ( void )
{
#define APPEND_WRITE_SIZE 4096

    if ( p_buf )
        rt_free ( p_buf );

    p_buf = rt_calloc ( 4096, 1 );

    if ( p_buf == 0 )
        return -1;

    uint32_t cnt  = TEST_FILE_LEN / APPEND_WRITE_SIZE;

    rt_tick_t t_b = rt_tick_get();

    while ( cnt-- )
    {
        fs_write_append ( "/sd/append.bin", p_buf, APPEND_WRITE_SIZE );
    }

    rt_tick_t t_e = rt_tick_get();
    float MB = ( float ) TEST_FILE_LEN / 1024.0 / 1024.0 ;
    float t = ( float ) ( t_e - t_b ) / 1000.0;
    float speed = MB / t;
    printf ( "[%d] write append %d KB take time:%d ms,speed %f MB/S\r\n", APPEND_WRITE_SIZE, ( TEST_FILE_LEN / 1024 ), ( int ) ( t_e - t_b ), speed );

    rt_free ( p_buf );
    p_buf = 0;

    return 0;
}

//连续写入指定位置
static int do_file_write_continue ( void )
{
#define CONTINUE_WRITE_SIZE 4096

    if ( p_buf )
        rt_free ( p_buf );

    p_buf = rt_calloc ( 4096, 1 );

    uint32_t cnt  = TEST_FILE_LEN / CONTINUE_WRITE_SIZE;
    uint32_t index = 0;

    if ( fs_get_file_access ( "/sd/continu.bin" ) )
        fs_delete_file ( "/sd/continu.bin" );

    rt_tick_t t_b = rt_tick_get();

    while ( cnt-- )
    {
        fs_write_with_create ( "/sd/continu.bin", index, p_buf, CONTINUE_WRITE_SIZE );
        index += CONTINUE_WRITE_SIZE;
    }

    rt_tick_t t_e = rt_tick_get();
    float MB = ( float ) TEST_FILE_LEN / 1024.0 / 1024.0 ;
    float t = ( float ) ( t_e - t_b ) / 1000.0;
    float speed = MB / t;
    printf ( "[%d] write continue %d KB take time:%d ms,speed %f MB/S\r\n", APPEND_WRITE_SIZE, ( TEST_FILE_LEN / 1024 ), ( int ) ( t_e - t_b ), speed );
    rt_free ( p_buf );
    p_buf = 0;

    return 0;
}

static int do_file_write_with_check ( void )
{
    if ( p_buf )
        rt_free ( p_buf );

    p_buf = ( uint8_t * ) rt_calloc ( 1, TEST_FILE_LEN );

    if ( p_buf == 0 )
        return -1;

    for ( int i = 0; i < TEST_FILE_LEN; i++ )
    {
        p_buf[i] = ( i / 512 ) + '0';
    }

    char path[32];

    rt_memset ( path, 0, 32 );
    rt_sprintf ( path, "/sd/test_check.bin" );

    if ( fs_get_file_access ( path ) )
        fs_delete_file ( path );

    fs_write_with_create ( path, 0, ( void * ) p_buf, TEST_FILE_LEN );
    int res = fs_write_data_check ( path, 0, ( void * ) p_buf, TEST_FILE_LEN );
    rt_kprintf ( "check %s\r\n", res == 0 ? "ok" : "failed" );

    rt_free ( p_buf );
    p_buf = 0;
    
    return 0;
}

static int fs_init ( void )
{
    fs_prepare();
    return 0;
}

/* 导出到 MSH 命令列表 */

MSH_CMD_EXPORT ( do_mkfs, do mkfs );
MSH_CMD_EXPORT ( do_mount, mount );
MSH_CMD_EXPORT ( do_unmount, unmount );
MSH_CMD_EXPORT ( do_delete_file, delete file );
MSH_CMD_EXPORT ( do_delete_dir, delete dir );
MSH_CMD_EXPORT ( do_create_dir, create dir );
MSH_CMD_EXPORT ( do_create_file, create file );
MSH_CMD_EXPORT ( do_write, write );
MSH_CMD_EXPORT ( do_write_with_create, write create );
MSH_CMD_EXPORT ( do_write_append, write append );
MSH_CMD_EXPORT ( do_read, read all );
MSH_CMD_EXPORT ( do_copy_file, copy file );
MSH_CMD_EXPORT ( do_copy_dir_file, copy dir file );
MSH_CMD_EXPORT ( do_get_state, get state );
MSH_CMD_EXPORT ( do_get_file_cnt, file cnt );
MSH_CMD_EXPORT ( do_event_cb, event cb );
MSH_CMD_EXPORT ( do_write_replace, do replace );
MSH_CMD_EXPORT ( do_file_test, file test );
MSH_CMD_EXPORT ( do_file_write_append, file append write );
MSH_CMD_EXPORT ( do_file_write_continue, file continue write );
MSH_CMD_EXPORT ( do_file_write_with_check, write check );

/* 导出到初始化列表 */
INIT_APP_EXPORT ( fs_init );
